// 版权归Go作者所有。版权所有。
// 此源代码的使用受BSD样式
// 许可证的约束，该许可证可以在许可证文件中找到。

package runtime

import (
	"internal/cpu"
	"runtime/internal/atomic"
	"unsafe"
)

const (
	// gcgonalization是
	// 标记为GOMAXPROCS的一部分的目标CPU利用率。
	gcGoalUtilization = 0.30

	// gcBackgroundUtilization是后台
	// 标记的固定CPU使用率。它必须是<=gc目标化。
	// GCG目标化和gcBackgroundUtilization之间的差异将由
	// mark assists弥补。调度程序的目标是在
	// 目标的50%范围内使用。
	// 
	// 将其设置为<gcgonalization可避免在没有辅助时使触发器
	// 饱和反馈控制器，从而使其能够
	// 更好地控制CPU和堆的增长。然而，差距越大，
	// 预计发生的突变子协助越多，这会影响
	// 突变子延迟。
	gcBackgroundUtilization = 0.25

	// gcCreditSlack是更新gcController.scanWork和
	// gcController.bgScanCredit之前可以在本地累积的扫描工作点数。值越低，助攻率越准确，助攻成功窃取背景学分的可能性越大。较高的值会减少内存
	// 争用。
	gcCreditSlack = 2000

	// gcAssistTimeSlack是在更新gcController.assistTime之前，
	// 可以在P上累积的突变辅助时间的纳秒数。
	gcAssistTimeSlack = 5000

	// gcOverAssistWork确定一个GC-
	// assist在发生协助时执行多少额外的扫描工作单元。这通过预付未来分配的这么多字节来分摊
	// 的成本。
	gcOverAssistWork = 64 << 10

	// defaultHeapMinimum是GOGC==100的heapMinimum的值。
	defaultHeapMinimum = 4 << 20
)

func init() {
	if offset := unsafe.Offsetof(gcController.heapLive); offset%8 != 0 {
		println(offset)
		throw("gcController.heapLive not aligned to 8 bytes")
	}
}

// gcController实现GC起搏控制器，该控制器确定何时触发并发垃圾收集以及标记量
// 在mutator辅助和背景标记中要做的工作。
// 
// 使用反馈控制算法调整gcController.trigger 
// 根据每个周期的堆增长和GC CPU利用率进行触发。
// 此算法优化堆增长以匹配GOGC，优化CPU 
// 辅助和背景标记之间的利用率为
// GOMAXPROCS的25%。该算法的高层设计见
// athttps:
// 
// gcController的所有字段仅在单个标记
// 循环期间使用。
var gcController gcControllerState

type gcControllerState struct {
	// 从$GOGC初始化。GOGC=off表示无GC。
	gcPercent int32

	_ uint32 // 填充，因此下面的64位值是8字节对齐的

	// heapMinimum是触发GC的最小堆大小。
	// 对于小堆，这将覆盖通常的GOGC*活动集规则。
	// 
	// 当有一个非常小的活动集但有大量的分配时，只需
	// 当堆达到GOGC*live时进行收集，就会导致许多GC 
	// 循环和较高的每GC总开销。这个最小值在保持堆相当小的同时，按GC开销分摊
	// 。
	// 
	// 在初始化过程中，此设置为4MB*GOGC/100。在
	// GOGC==0的情况下，这会将heapMinimum设置为0，即使在堆大小很小的情况下，也会产生恒定的
	// 集合，这对
	// 调试非常有用。
	heapMinimum uint64

	// triggerRatio是触发标记的堆增长率。
	// 
	// 例如，如果这是0.6，那么当活动
	// 堆达到上一个周期
	// 标记的堆大小的1.6倍时，应该启动GC。这应该是≤ GOGC/100，因此触发器
	// 堆大小小于目标堆大小。这是在标记终止期间为下一个周期的触发器设置的
	// 。
	// 
	// 受mheap_u.锁或STW保护。
	triggerRatio float64

	// 触发器是触发标记的堆大小。
	// 
	// 当他活着的时候≥ 触发后，标记阶段将开始。
	// 这也是堆大小，按比例扫描
	// 必须完成。
	// 
	// 这是根据标记终止期间的触发器比率
	// 为下一个周期的触发器计算得出的。
	// 
	// 受mheap_u.锁或STW保护。
	trigger uint64

	// heapGoal是下一次GC结束时的目标heaplife。
	// 如果禁用，则设置为^uint64（0）。
	// 
	// 以原子方式读写，除非世界停止。
	heapGoal uint64

	// lastHeapGoal是上一个GC的heapGoal值。
	// 请注意，这与heapGoal的上一个值
	// 不同，因为它可能会在例如gcPercent发生变化时发生变化。
	// 
	// 在世界停止或mheap_u2;锁定的情况下读写。
	lastHeapGoal uint64

	// heapLive是GC认为活动的字节数。
	// 即：由最新的GC加上此后分配的
	// 保留。希普利夫≤ memstats.heapAlloc，因为heapAlloc包含尚未扫描的未标记对象（因此在扫描时上升，在扫描时分配，在扫描时下降），而heapLive排除这些对象（因此仅在GCs之间上升）。
	// 
	// 这是在没有锁定的情况下以原子方式更新的。为了减少
	// 争用，仅当从
	// 一个mcentral获取一个span时才会更新，此时它统计该span中所有未分配的
	// 插槽（在mcache从该
	// mcentral获得另一个span之前，将分配这些插槽）。因此，它稍微高估了“真实”的活动
	// 堆大小。高估比低估要好，因为1）这比
	// 更早触发GC，而不是可能太晚；2）这比
	// 导致保守GC率，而不是
	// 可能太低的GC率。
	// 
	// 读取同样应该是原子的（或在STW期间）。
	// 
	// 每当更新时，调用traceHeapAlloc（）和
	// 此gcControllerState的Revision（）方法。
	heapLive uint64

	// heapScan是“可扫描”堆的字节数。此
	// 是活动堆（按heapLive计数），但省略了
	// 没有扫描对象和对象的扫描尾。
	// 
	// 每当更新时，调用此gcControllerState的
	// revision（）方法。
	// 
	// 以原子方式或在世界停止时读写。
	heapScan uint64

	// heapMarked是上一个
	// GC标记的字节数。标记终止后，heapLive==heapMarked，但与heapLive不同，heapMarked在下一次标记终止前不会改变。
	heapMarked uint64

	// scanWork是本周期执行的总扫描工时。此
	// 在循环期间以原子方式更新。更新发生在
	// 绑定批中，因为它在整个周期中都是写入和读取
	// 的。在循环结束时，
	// 大部分保留堆是可扫描的。
	// 
	// 当前这是扫描的堆字节数。对于大多数用途，这是一个不透明的工作单元，但对于估算来说，定义很重要。
	scanWork int64

	// bgScanCredit是
	// 并发后台扫描累积的扫描工作积分。此信用证由
	// 后台扫描累积，并被mutator assists窃取。这是
	// 原子更新。更新是在有限制的批中进行的，因为
	// 在整个周期中都会写入和读取更新。
	bgScanCredit int64

	// assistTime是在这个周期内，在突变辅助
	// 中花费的纳秒数。这是原子更新的。更新
	// 在有限制的批处理中发生，因为它在整个周期中同时被写入和读取
	// 。
	assistTime int64

	// 专用标记时间是在该周期内专用
	// 标记工人所花费的纳秒数。这是在并发标记阶段结束时以原子方式更新的
	// 。
	dedicatedMarkTime int64

	// 分数标记时间是此循环中在
	// 分数标记工作进程中花费的纳秒数。这将在整个周期内自动更新
	// 并且如果
	// 分数标记辅助程序当前未运行，则它将是最新的。
	fractionalMarkTime int64

	// idleMarkTime是此循环中用于空闲标记
	// 的纳秒数。在整个
	// 周期内，这是原子更新的。
	idleMarkTime int64

	// markStartTime是以纳秒为单位的绝对启动时间
	// 用于协助和背景标记工作人员启动。
	markStartTime int64

	// IficatedMarkWorkersRequired是需要启动的专用标记
	// 工作人员的数量。这是在每个循环开始时计算的，并在开始使用专用标记时原子递减。
	dedicatedMarkWorkersNeeded int64

	// assistWorkPerByte是应由mutator assists执行的扫描工时与分配的
	// 字节的比率。这是
	// 在每个周期开始时计算并更新每个
	// 更新heapScan的时间。
	// 
	// 存储为uint64，但实际上是浮点64。使用
	// float64frombits获取值。
	// 
	// 以原子方式读写。
	assistWorkPerByte uint64

	// 辅助字节数为1/辅助字节数。
	// 
	// 存储为uint64，但实际上是浮点64。使用
	// float64frombits获取值。
	// 
	// 以原子方式读写。
	// 
	// 请注意，由于这是从assistWorkPerByte独立读写的
	// 用户可能会注意到
	// 这两个值之间存在偏差，因此这种状态应该是安全的。
	assistBytesPerWork uint64

	// 分步解决目标是挂钟
	// 在
	// 上的分步标记工作程序中应该花费的时间的分数，每个P没有运行专用工作程序。
	// 
	// 例如，如果利用率目标为25%，并且有
	// 没有专门的工人，则为0.25。如果目标为
	// 25%，则有一名专职工人，而GOMAXPROCS为5，
	// 这将是0.05，以弥补缺少的5%。
	// 
	// 如果该值为零，则不需要分数工人。
	fractionalUtilizationGoal float64

	_ cpu.CacheLinePad
}

func (c *gcControllerState) init(gcPercent int32) {
	c.heapMinimum = defaultHeapMinimum

	// 设置合理的初始GC触发器。
	c.triggerRatio = 7 / 8.0

	// 伪造一个heapMarked值，使其看起来像是
	// heapMinimum是heapMarked的适当增长。
	// 这将用于计算初始GC目标。
	c.heapMarked = uint64(float64(c.heapMinimum) / (1 + c.triggerRatio))

	// 这还将计算并设置GC触发器和目标。
	c.setGCPercent(gcPercent)
}

// startCycle重置GC控制器的状态，并计算新GC循环的估计值
// 。调用者必须持有worldsema，并且必须停止world 
// 。
func (c *gcControllerState) startCycle() {
	c.scanWork = 0
	c.bgScanCredit = 0
	c.assistTime = 0
	c.dedicatedMarkTime = 0
	c.fractionalMarkTime = 0
	c.idleMarkTime = 0

	// 确保堆目标至少略大于当前活动堆大小。如果GC 
	// 启动延迟，或者将gcController.heapLive 
	// 推到触发器上方的分配过大，或者触发器非常接近
	// GOGC，则可能不会出现这种情况。助攻与此距离成正比，因此执行
	// 最小距离，即使这意味着稍微超过GOGC目标
	// 的距离。
	if c.heapGoal < c.heapLive+1024*1024 {
		c.heapGoal = c.heapLive + 1024*1024
	}

	// 计算背景标记使用目标。总的来说，
	// 这可能并不确切。我们对
	// 专职员工的数量进行四舍五入，使利用率接近
	// 25%。对于小型GomaxProc，这将引入太多
	// 错误，因此在这种情况下，我们添加了分数工人。
	totalUtilizationGoal := float64(gomaxprocs) * gcBackgroundUtilization
	c.dedicatedMarkWorkersNeeded = int64(totalUtilizationGoal + 0.5)
	utilError := float64(c.dedicatedMarkWorkersNeeded)/totalUtilizationGoal - 1
	const maxUtilError = 0.3
	if utilError < -maxUtilError || utilError > maxUtilError {
		// 四舍五入使我们的目标降低了30%以上。当
		// gcBackgroundUtilization为25%时，
		// GOMAXPROCS<=3或GOMAXPROCS=6会出现这种情况。启用部分
		// 工人补偿。
		if float64(c.dedicatedMarkWorkersNeeded) > totalUtilizationGoal {
			// 有太多敬业的员工。
			c.dedicatedMarkWorkersNeeded--
		}
		c.fractionalUtilizationGoal = (totalUtilizationGoal - float64(c.dedicatedMarkWorkersNeeded)) / float64(gomaxprocs)
	} else {
		c.fractionalUtilizationGoal = 0
	}

	// 在STW模式下，我们只需要敬业的员工。
	if debug.gcstoptheworld > 0 {
		c.dedicatedMarkWorkersNeeded = int64(gomaxprocs)
		c.fractionalUtilizationGoal = 0
	}

	// 清除per-P状态
	for _, p := range allp {
		p.gcAssistTime = 0
		p.gcFractionalMarkTime = 0
	}

	// 计算在整个周期内更新的控件的初始值
	// 。
	c.revise()

	if debug.gcpacertrace > 0 {
		assistRatio := float64frombits(atomic.Load64(&c.assistWorkPerByte))
		print("pacer: assist ratio=", assistRatio,
			" (scan ", gcController.heapScan>>20, " MB in ",
			work.initialHeapLive>>20, "->",
			c.heapGoal>>20, " MB)",
			" workers=", c.dedicatedMarkWorkersNeeded,
			"+", c.fractionalUtilizationGoal, "\n")
	}
}

// 修订更新GC循环期间的辅助比率，以说明
// 改进的估计。无论何时更新gcController.heapScan、
// gcController.heapLive或gcController.heapGoal，都应调用此函数。
// 同时调用是安全的，但它可能会与其他调用竞争以进行修改。
// 
// 这场比赛的结果是，两个辅助比率值可能不符合
// 的要求，或者可能过时。在实践中，这是可以的，因为辅助比率在整个GC循环中缓慢移动，并且辅助比率是一个最大努力的启发式。此外，启发式的任何部分都不依赖于
// 这两个辅助比值是彼此的精确倒数，因为
// 这两个值用于转换来自不同来源的值。
// 
// 这种活泼最糟糕的结果是我们可能会错过更大的转变
// 在比例上（比如，如果我们决定对
// 硬堆目标采取更积极的步伐），但即使是这个“硬目标”也是最好的努力（参见#40460）。
// 专门的GC应确保我们不会超过硬目标太多
// 在极少数情况下，我们确实超过了硬目标。
// 
// 只应在GCBlackEnabled时调用它！=0（因为此
// 是启用协助时，并且必要的统计信息
// 可用）。
func (c *gcControllerState) revise() {
	gcPercent := c.gcPercent
	if gcPercent < 0 {
		// 如果GC被禁用，但我们正在运行一个强制GC，
		// 就好像GOGC对于下面的计算是巨大的。
		gcPercent = 100000
	}
	live := atomic.Load64(&c.heapLive)
	scan := atomic.Load64(&c.heapScan)
	work := atomic.Loadint64(&c.scanWork)

	// 假设我们处于软目标之下。假设堆处于稳定状态，则在
	// heapGoal处调整GC的速度以完成。
	heapGoal := int64(atomic.Load64(&c.heapGoal))

	// 计算预期的剩余扫描工作量。
	// 
	// 这是根据预期的
	// 稳态可扫描堆估计的。例如，对于
	// GOGC=100，可扫描堆中只有一半是预期为活动的，所以这就是我们的目标。
	// 
	// （这是一个浮点计算，以避免在
	// 100*堆扫描上溢出。）
	scanWorkExpected := int64(float64(scan) * 100 / float64(100+gcPercent))

	if int64(live) > heapGoal || work > scanWorkExpected {
		// 我们已经超过了软目标，或者我们已经完成了比预期更多的扫描
		// 工作。调整GC的速度，以便在最坏的情况下，它将完成艰难的目标。
		const maxOvershoot = 1.1
		heapGoal = int64(float64(heapGoal) * maxOvershoot)

		// 计算剩余扫描功的上限。
		scanWorkExpected = int64(scan)
	}

	// 计算剩余扫描工时估算。
	// 
	// 注意，我们目前将GC期间的分配计算为
	// 可扫描堆（heapScan）和已完成的扫描工作
	// （scanWork），因此，分配将在软体制下缓慢改变这种差异，而在硬体制下则根本不会。
	scanWorkRemaining := scanWorkExpected - work
	if scanWorkRemaining < 1000 {
		// 我们对
		// 剩余的扫描工作设置了一个任意的下限，因为如果我们目标定得高一点，
		// 我们可能会错过一点。
		// 
		// 我们*确实*需要强制执行这至少是1，
		// 因为标记是活泼的，双扫描对象
		// 可以合法地使剩余的扫描工作
		// 阴性，即使在硬目标制度下也是如此。
		scanWorkRemaining = 1000
	}

	// 计算剩余堆距离。
	heapRemaining := heapGoal - int64(live)
	if heapRemaining <= 0 {
		// 这不应该发生，但如果发生，请避免将
		// 除以零或将辅助设置为负值。
		heapRemaining = 1
	}

	// 计算变异辅助比率，因此当变异器
	// 将剩余的堆字节分配给heapGoal时，它将
	// 完成（或窃取）剩余的扫描工作。
	// 请注意，辅助比率值是按原子方式更新的
	// 但不是一起更新的。这意味着两个值之间可能存在一定程度的偏差。这通常是正常的，因为
	// 值在GC 
	// 循环过程中移动相对缓慢。
	assistWorkPerByte := float64(scanWorkRemaining) / float64(heapRemaining)
	assistBytesPerWork := float64(heapRemaining) / float64(scanWorkRemaining)
	atomic.Store64(&c.assistWorkPerByte, float64bits(assistWorkPerByte))
	atomic.Store64(&c.assistBytesPerWork, float64bits(assistBytesPerWork))
}

// endCycle计算下一个周期的触发比。
// userForced表示当前GC循环是否被应用程序强制。
func (c *gcControllerState) endCycle(userForced bool) float64 {
	if userForced {
		// 强制GC意味着此循环不是从
		// 触发器开始的，因此它完成的位置不好
		// 有关如何调整触发器的信息。
		// 放在原处就行了。
		return c.triggerRatio
	}

	// 触发控制器的比例响应增益。
	// 必须在[0,1]中。较低的值可消除瞬态效应，但
	// 响应相位变化所需的时间较长。较高的值
	// 对相位变化反应迅速，但受
	// 瞬态变化的影响较大。接近1的值可能不稳定。
	const triggerGain = 0.5

	// 计算下一周期触发比。首先，计算本周期的
	// /“错误”；也就是说，触发器
	// 距离它应该达到的距离有多远，这说明了堆
	// 的增长和GC CPU的利用率。我们计算此周期内的实际堆
	// 增长，并按距离目标CPU利用率
	// 的距离进行缩放（如果我们有期望的CPU利用率，则估计堆
	// 增长）。
	// 此估计与基于GOGC的目标
	// 堆增长之间的差异是错误的。
	goalGrowthRatio := c.effectiveGrowthRatio()
	actualGrowthRatio := float64(c.heapLive)/float64(c.heapMarked) - 1
	assistDuration := nanotime() - c.markStartTime

	// 假设背景标记达到其利用率目标。
	utilization := gcBackgroundUtilization
	// 添加辅助利用率；避免被零除。
	if assistDuration > 0 {
		utilization += float64(c.assistTime) / float64(assistDuration*int64(gomaxprocs))
	}

	triggerError := goalGrowthRatio - c.triggerRatio - utilization/gcGoalUtilization*(actualGrowthRatio-c.triggerRatio)

	// 最后，我们通过这个错误
	// 由比例增益阻尼。
	triggerRatio := c.triggerRatio + triggerGain*triggerError

	if debug.gcpacertrace > 0 {
		// 打印设计中的控制器状态
		// 文档。
		H_m_prev := c.heapMarked
		h_t := c.triggerRatio
		H_T := c.trigger
		h_a := actualGrowthRatio
		H_a := c.heapLive
		h_g := goalGrowthRatio
		H_g := int64(float64(H_m_prev) * (1 + h_g))
		u_a := utilization
		u_g := gcGoalUtilization
		W_a := c.scanWork
		print("pacer: H_m_prev=", H_m_prev,
			" h_t=", h_t, " H_T=", H_T,
			" h_a=", h_a, " H_a=", H_a,
			" h_g=", h_g, " H_g=", H_g,
			" u_a=", u_a, " u_g=", u_g,
			" W_a=", W_a,
			" goalΔ=", goalGrowthRatio-h_t,
			" actualΔ=", h_a-h_t,
			" u_a/u_g=", u_a/u_g,
			"\n")
	}

	return triggerRatio
}

// 登记工人鼓励另一个专用的标记工人在
// 上启动另一个P（如果有备用工人插槽）。当更多的工作可用时，putfull 
// 将使用它。
// 
// go:nowritebarrier 
func (c *gcControllerState) enlistWorker() {
	// 如果有空闲的Ps，唤醒一个，这样它将运行空闲的工作进程。
	// 注意：这可能导致死锁。见golang.org/issue/19112。
	// 
	// /if-atomic.Load（&sched.npidle）！=0&&atomic.Load（&sched.nmspining）==0{
	// wakep（）
	// /返回
	// }

	// 没有空闲的Ps。如果我们需要更多专用的工作线程，
	// 尝试抢占正在运行的P，以便它将切换到工作线程。
	if c.dedicatedMarkWorkersNeeded <= 0 {
		return
	}
	// 随机选择另一个P进行抢占。
	if gomaxprocs <= 1 {
		return
	}
	gp := getg()
	if gp == nil || gp.m == nil || gp.m.p == 0 {
		return
	}
	myID := gp.m.p.ptr().id
	for tries := 0; tries < 5; tries++ {
		id := int32(fastrandn(uint32(gomaxprocs - 1)))
		if id >= myID {
			id++
		}
		p := allp[id]
		if p.status != _Prunning {
			continue
		}
		if preemptone(p) {
			return
		}
	}
}

// findRunnableGCWorker返回u_u的背景标记worker，如果它
// 应该运行。只有在gcBlackenEnabled！=0
func (c *gcControllerState) findRunnableGCWorker(_p_ *p) *g {
	if gcBlackenEnabled == 0 {
		throw("gcControllerState.findRunnable: blackening not enabled")
	}

	if !gcMarkWorkAvailable(_p_) {
		// 现在没有工作要做。这可能发生在
		// 标记阶段结束时，此时仍有
		// 辅助渐减。现在不要麻烦运行worker 
		// 因为它会立即返回。
		return nil
	}

	// 在我们承诺在下面运行之前，请先抓到一名工作人员。
	node := (*gcBgMarkWorkerNode)(gcBgMarkWorkerPool.pop())
	if node == nil {
		// 每个P至少有一个工人，因此通常有足够的工人在所有P上运行，如果需要的话。但是，一旦
		// 一个工人进入gcMarkDone，它就可以在不重新加入
		// 池的情况下停车，从而释放一个没有相应工人的P。
		// gcMarkDone从不依赖另一个工人来工作，因此在这里什么都不做是安全的。
		// 
		// 如果gcMarkDone在没有完成标记阶段的情况下退出，
		// 它将始终使用排队的全局工作。因此，P 
		// 将立即有资格重新运行刚刚使用的工作程序G 
		// 以确保工作能够完成。
		return nil
	}

	decIfPositive := func(ptr *int64) bool {
		for {
			v := atomic.Loadint64(ptr)
			if v <= 0 {
				return false
			}

			if atomic.Casint64(ptr, v, v-1) {
				return true
			}
		}
	}

	if decIfPositive(&c.dedicatedMarkWorkersNeeded) {
		// 此P现在专用于标记，直到
		// 并发标记阶段结束。
		_p_.gcMarkWorkerMode = gcMarkWorkerDedicatedMode
	} else if c.fractionalUtilizationGoal == 0 {
		// 不需要零工。
		gcBgMarkWorkerPool.push(&node.node)
		return nil
	} else {
		// 这个P是否落后于分数利用率
		// 目标？
		// 
		// 这应该与PollFractalWorkerXit保持同步。没有。不需要运行零碎的工作程序。
		delta := nanotime() - c.markStartTime
		if delta > 0 && float64(_p_.gcFractionalMarkTime)/float64(delta) > c.fractionalUtilizationGoal {
			gcBgMarkWorkerPool.push(&node.node)
			return nil
		}
		// 运行一个部分工作程序。
		_p_.gcMarkWorkerMode = gcMarkWorkerFractionalMode
	}

	// 运行后台标记工作程序。
	gp := node.gp.ptr()
	casgstatus(gp, _Gwaiting, _Grunnable)
	if trace.enabled {
		traceGoUnpark(gp, 0)
	}
	return gp
}

// commit设置触发比率并更新从中派生的所有内容：绝对触发器、堆目标、标记调整、
// 和扫描调整。
// 
// 可以随时调用。如果GC处于ABCFDG 
// 
// 这取决于gcPercent、gcController.heapMarked和
// gcController.heapLive。这些必须是最新的。
// 
// mheap_u2;。必须持有锁，否则世界必须停止。
func (c *gcControllerState) commit(triggerRatio float64) {
	assertWorldStoppedOrLockHeld(&mheap_.lock)

	// 计算下一个GC目标，即分配的堆
	// 比上一个
	// 循环标记的堆增长了GOGC/100。
	goal := ^uint64(0)
	if c.gcPercent >= 0 {
		goal = c.heapMarked + c.heapMarked*uint64(c.gcPercent)/100
	}

	// 设置触发比率，上限为合理界限。
	if c.gcPercent >= 0 {
		scalingFactor := float64(c.gcPercent) / 100
		// 确保总有一点余量，这样变异辅助比就不是无穷大了。
		maxTriggerRatio := 0.95 * scalingFactor
		if triggerRatio > maxTriggerRatio {
			triggerRatio = maxTriggerRatio
		}

		// 如果我们让triggerRatio太低，那么如果应用程序
		// 分配得非常快，我们可能最终会遇到这样的情况
		// 在几乎总是打开的GC中分配黑色。
		// 这导致了堆的增长，最终导致RSS的增加。通过将我们的上限设置为>0，我们本质上是
		// 说我们可以在GC期间使用更多CPU来防止RSS的增长。
		// 
		// 当前常数是根据经验选择的：给定一个足够的
		// 快速/可扩展的分配器，具有48 Ps，可以将触发比
		// 驱动到<0.05，与没有此分配器相比，此常量使应用程序保留相同的峰值
		// RSS。
		minTriggerRatio := 0.6 * scalingFactor
		if triggerRatio < minTriggerRatio {
			triggerRatio = minTriggerRatio
		}
	} else if triggerRatio < 0 {
		// gcPercent<0，所以请确保我们没有得到负的
		// triggerRatio。这种情况在实践中是不可能发生的，
		// 也不重要，因为如果gcPercent<0，那么我们就不会
		// 曾经在这个函数中进一步使用triggerRatio，但是让我们
		// 在这里只是防御；触发比为负几乎是
		// /当然不可取。
		triggerRatio = 0
	}
	c.triggerRatio = triggerRatio

	// 根据触发比率计算绝对GC触发器。
	// 
	// 当分配的堆具有
	// 增长的触发比超过标记的堆大小时，我们触发下一个GC循环。
	trigger := ^uint64(0)
	if c.gcPercent >= 0 {
		trigger = uint64(float64(c.heapMarked) * (1 + triggerRatio))
		// 不要在最小堆大小以下触发。
		minTrigger := c.heapMinimum
		if !isSweepDone() {
			// 并发扫描发生在堆增长中
			// 从gcController.heaplife触发，因此确保
			// 并发扫描具有一些堆增长
			// 在开始下一个GC循环之前执行扫描。
			sweepMin := atomic.Load64(&c.heapLive) + sweepMinHeapDistance
			if sweepMin > minTrigger {
				minTrigger = sweepMin
			}
		}
		if trigger < minTrigger {
			trigger = minTrigger
		}
		if int64(trigger) < 0 {
			print("runtime: heapGoal=", c.heapGoal, " heapMarked=", c.heapMarked, " gcController.heapLive=", c.heapLive, " initialHeapLive=", work.initialHeapLive, "triggerRatio=", triggerRatio, " minTrigger=", minTrigger, "\n")
			throw("trigger underflow")
		}
		if trigger > goal {
			// 触发器比率始终小于GOGC/100，但
			// 触发器的其他边界可能会使其升高。
			// 也要把目标往上推。
			goal = trigger
		}
	}

	// 致力于触发器和目标。
	c.trigger = trigger
	atomic.Store64(&c.heapGoal, goal)
	if trace.enabled {
		traceHeapGoal()
	}

	// 更新标记起搏。
	if gcphase != _GCoff {
		c.revise()
	}

	// 更新扫描速度。
	if isSweepDone() {
		mheap_.sweepPagesPerByte = 0
	} else {
		// 并发扫描需要在分配的堆到达GC 
		// 页面。计算分配的每个字节中正在使用的页面与扫描
		// 触发器时扫描所有正在使用的
		// 的比率，考虑到
		// 某些页面可能已经被扫描。
		heapLiveBasis := atomic.Load64(&c.heapLive)
		heapDistance := int64(trigger) - int64(heapLiveBasis)
		// 添加一点边距，这样舍入错误和
		// 并发扫描在GC启动时不太可能使页面未被扫描。
		heapDistance -= 1024 * 1024
		if heapDistance < _PageSize {
			// 避免将扫描比率设置得过高
			heapDistance = _PageSize
		}
		pagesSwept := atomic.Load64(&mheap_.pagesSwept)
		pagesInUse := atomic.Load64(&mheap_.pagesInUse)
		sweepDistancePages := int64(pagesInUse) - int64(pagesSwept)
		if sweepDistancePages <= 0 {
			mheap_.sweepPagesPerByte = 0
		} else {
			mheap_.sweepPagesPerByte = float64(sweepDistancePages) / float64(heapDistance)
			mheap_.sweepHeapLiveBasis = heapLiveBasis
			// 最后写入PagesWeptBasis，因为此
			// 发出并发扫描信号以重新计算其债务。
			atomic.Store64(&mheap_.pagesSweptBasis, pagesSwept)
		}
	}

	gcPaceScavenger()
}

// effectiveGrowthRatio返回当前有效堆增长
// 比率（GOGC/100），该比率基于上一个GC标记的堆和当前GC的
// heapGoal。
// 
// 这可能与gcPercent/100不同，因为gcPercent的上限和下限不同。例如，如果堆小于
// heapMinimum，则该值可能高于gcPercent/100。
// 
// mheap_u2;。必须持有锁，否则世界必须停止。
func (c *gcControllerState) effectiveGrowthRatio() float64 {
	assertWorldStoppedOrLockHeld(&mheap_.lock)

	egogc := float64(atomic.Load64(&c.heapGoal)-c.heapMarked) / float64(c.heapMarked)
	if egogc < 0 {
		// 不应该发生，只是以防万一。
		egogc = 0
	}
	return egogc
}

// setGCPercent更新gcPercent和所有相关起搏器状态。
// 返回gcPercent的旧值。
// 
// 世界必须被阻止，否则必须被锁住。
func (c *gcControllerState) setGCPercent(in int32) int32 {
	assertWorldStoppedOrLockHeld(&mheap_.lock)

	out := c.gcPercent
	if in < 0 {
		in = -1
	}
	c.gcPercent = in
	c.heapMinimum = defaultHeapMinimum * uint64(c.gcPercent) / 100
	// 根据GCG百分比变化更新配速。
	c.commit(c.triggerRatio)

	return out
}

// go:linkname setGCPercent runtime/debug.setGCPercent 
func setGCPercent(in int32) (out int32) {
	// 在获取堆锁后在系统堆栈上运行。
	systemstack(func() {
		lock(&mheap_.lock)
		out = gcController.setGCPercent(in)
		unlock(&mheap_.lock)
	})

	// 如果我们刚刚禁用了GC，请等待任何并发GC标记变为
	// 完成，这样我们总是在不运行GC的情况下返回。
	if in < 0 {
		gcWaitOnMark(atomic.Load(&work.cycles))
	}

	return out
}

func readGOGC() int32 {
	p := gogetenv("GOGC")
	if p == "off" {
		return -1
	}
	if n, ok := atoi32(p); ok {
		return n
	}
	return 100
}
